using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using EnvDTE80;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using TeamReview.Services;
using TeamReview.Settings;
using System.Drawing;
using System.Threading;

namespace TeamReview.UI
{
	public partial class CreateWorkItemForm : Form
	{

		#region Variables

		private CodeSelection _codeSelection;
		private readonly DTE2 shellDTE;
		private Project workItemSubmissionProject;
		private readonly SettingsReaderWriter settings = new SettingsReaderWriter();
		private TeamSetting teamSetting;

		#endregion


		#region Constructor

		public CreateWorkItemForm(DTE2 shell, CodeSelection codeSelection)
		{
			InitializeComponent();
			shellDTE = shell;
			_codeSelection = codeSelection;
			RestoreWindow();
			TFSService.TeamProjectChanged += new TeamProjectChangeEventHandler(TFSService_TeamProjectChanged);

			//Binidng queries can kick off a number of threads to bind other
			//drop downs so we put it first in sequence for better percieved performance
			BindQueries();

			BindToolTips();
			BindTextBoxes();
		}

		#endregion

		#region Private "User Preferences" Methods (Settings)

		private void UpdateWindowSettings()
		{
			WindowSetting ws = settings.Get(this.Name);

			if (ws == null)
			{
				ws = new WindowSetting();
				settings.Add(ws);
			}

			ws.Name = this.Name;
			ws.Width = Width;
			ws.Height = Height;
			ws.WindowPoint = Location;
			ws.WindowState = WindowState;
		}

		private void UpdateTeamSettings()
		{

			TeamProject project = TFSService.GetTeamProject();
			TeamSetting ts = settings.Get(project);
			if (ts == null)
			{
				ts = new TeamSetting();
				ts.ArtifactUri = project.ArtifactUri.ToString();
				settings.Add(ts);
			}

			if (_cmbLinkedItem.SelectedItem != null)
				ts.ItemAssociated = ((BindingItemCodeReview)_cmbLinkedItem.SelectedItem).Id;
			else
				ts.ItemAssociated = 0;

			if (_cmbQuery.SelectedItem != null)
				ts.QueryGuid = ((BindingItemQuery)_cmbQuery.SelectedItem).QueryGuid;
			else
				ts.QueryGuid = Guid.Empty;

			if (_cmbAssignedUser.SelectedItem != null)
				ts.UserAssigned = _cmbAssignedUser.SelectedItem.ToString();
			else
				ts.UserAssigned = String.Empty;

			if (_cmbPriority.SelectedItem != null)
				ts.Priority = _cmbPriority.SelectedItem.ToString();
			else
				ts.Priority = String.Empty;

			ts.ChangeSet = textBoxChangeset.Text;
		}

		private void RestoreWindow()
		{
			WindowSetting ws = settings.Get(this.Name);
			TeamProject project = TFSService.GetTeamProject();
			teamSetting = settings.Get(project);

			if (teamSetting == null)
			{
				teamSetting = new TeamSetting();
				teamSetting.ArtifactUri = project.ArtifactUri.ToString();
			}

			if (ws == null)
				return;

			this.StartPosition = FormStartPosition.Manual;
			Width = ws.Width;
			Height = ws.Height;
			Location = ws.WindowPoint;
			WindowState = ws.WindowState;
		}

		#endregion

		#region Event Handlers


		void TFSService_TeamProjectChanged()
		{
			Close();
		}

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);
			Text = "Create Code Review Response";
		}

		private void btnCancel_Click(object sender, EventArgs e)
		{
			Close();
		}

		private void btnSave_Click(object sender, EventArgs e)
		{
			_errorProvider1.Clear();
			bool validUser = ValidateAssignedUser();
			bool validTitle = ValidateTitle();
			bool validDescription = ValidateDescription();
			bool validPriority = ValidatePriority();
			if (!validUser || !validTitle || !validDescription || !validPriority)
				return;

			Save();
			Close();
		}


		private void cmbQuery_SelectedIndexChanged(object sender, EventArgs e)
		{
			//Project quireied by the selected Query 
			Guid queryGuid = Guid.Empty;
			if (_cmbQuery.SelectedIndex >= 0)
				queryGuid = ((BindingItemQuery)_cmbQuery.SelectedItem).QueryGuid;

			if (queryGuid != Guid.Empty)
			{
				Project queryProject = TFSService.GetQueryProject(queryGuid);
				
				if (workItemSubmissionProject != queryProject)
				{
					workItemSubmissionProject = queryProject;					
					BindUsers();
					BindPriority();					
				}
				BindLinkedItems();
			}		
		}


		private void _cmbLinkedItem_SelectedIndexChanged(object sender, EventArgs e)
		{

			//Project tied to Code Review work item that is selected
			WorkItem relatedCodeReview = GetRelatedCodeReview();
			if (relatedCodeReview != null)
			{
				if (workItemSubmissionProject != relatedCodeReview.Project)
				{
					workItemSubmissionProject = relatedCodeReview.Project;
					BindUsers();
					BindPriority();
				}
			}
		}

		private void cmbAssignedUser_Validating(object sender, CancelEventArgs e)
		{
			ValidateAssignedUser();
		}

		private void txtTitle_Validating(object sender, CancelEventArgs e)
		{
			ValidateTitle();
		}


		private void txtDescription_Validating(object sender, CancelEventArgs e)
		{
			ValidateDescription();
		}


		private void textBoxChangeset_Click(object sender, EventArgs e)
		{
			this.SendToBack();
			Changeset cs = TFSService.VersionControlExt.FindChangeset();
			if (cs != null)
				textBoxChangeset.Text = cs.ChangesetId.ToString();

			this.BringToFront();
		}




		#endregion



		#region Data Binding

		private void BindLinkedItems()
		{
			_cmbLinkedItem.Text = String.Empty;
			_cmbLinkedItem.DataSource = null;
			if (_cmbQuery.SelectedItem != null)
			{
				Guid queryGuid = ((BindingItemQuery)_cmbQuery.SelectedItem).QueryGuid;
				BindingList<BindingItemCodeReview> bl = TFSService.GetAvailableQueryWorkItems(queryGuid);

				if (bl.Count == 0)
					return;

				_cmbLinkedItem.DataSource = bl;

				for (int i = 0; i < bl.Count; i++)
				{
					BindingItemCodeReview bicr = bl[i];
					if (bicr.Id == teamSetting.ItemAssociated)
						_cmbLinkedItem.SelectedIndex = i;
				}
			}
		}

		private void BindQueries()
		{
			_cmbQuery.Text = String.Empty;
			_cmbQuery.DataSource = null;
			BindingList<BindingItemQuery> bl = TFSService.GetAvailableTeamQueries();

			if (bl == null || bl.Count == 0)
				return;

			_cmbQuery.DataSource = bl;
			_cmbQuery.DisplayMember = "Name";

			for (int i = 0; i < bl.Count; i++)
			{
				if (bl[i].QueryGuid.Equals(teamSetting.QueryGuid))
				{
					_cmbQuery.SelectedIndex = _cmbQuery.Items.IndexOf(bl[i]);
					break;
				}
			}
		}

		private void BindUsers()
		{
			_cmbAssignedUser.Text = String.Empty;
			_cmbAssignedUser.Items.Clear();
			Project project = workItemSubmissionProject;

			List<string> users = TFSService.GetTeamUsers(project);
			if (users == null)
				return;

			_cmbAssignedUser.Items.AddRange(users.ToArray());
			if ((teamSetting != null) && (teamSetting.UserAssigned != null) && _cmbAssignedUser.Items.Count > 0)
				_cmbAssignedUser.SelectedIndex = _cmbAssignedUser.Items.IndexOf(teamSetting.UserAssigned);
		}

		private void BindPriority()
		{
			_cmbPriority.Text = String.Empty;
			_cmbPriority.DataSource = null;

			Project project = workItemSubmissionProject;
			string reason;
			if (!HasWorkItemDefinition(project, out reason))
				return;

			FieldDefinitionCollection fields = project.WorkItemTypes[Constants.CODE_REVIEW_ITEM_TYPE].FieldDefinitions;

			if (!fields.Contains(Constants.CODE_PRIORITY_FIELD))
			{
				_lblPriority.Font = new Font(_lblPriority.Font, FontStyle.Regular);
				_lblPriority.Refresh();
				Instrumentation.Trace(String.Format("To use the Priority drop down update the '{0}' work item type to the latest version from http://TeamReview.codeplex.com.", Constants.CODE_REVIEW_ITEM_TYPE));
				return;
			}

			AllowedValuesCollection ac = fields[Constants.CODE_PRIORITY_FIELD].AllowedValues;
			if (ac != null)
				_cmbPriority.DataSource = ac;

			if ((teamSetting != null) && !String.IsNullOrEmpty(teamSetting.Priority) && _cmbPriority.Items.Count > 0)
				_cmbPriority.SelectedIndex = _cmbPriority.Items.IndexOf(teamSetting.Priority);

		}

		private void BindTextBoxes()
		{
			_txtSnippet.Text = _codeSelection.Snippet;
			if (String.IsNullOrEmpty(_codeSelection.CodeMember))
				_txtTitle.Text = String.Format("{0} {1}-{2}", _codeSelection.CodeFile, _codeSelection.StartLine, _codeSelection.EndLine);
			else
				_txtTitle.Text = String.Format("{0} {1}-{2}", _codeSelection.CodeMember, _codeSelection.StartLine, _codeSelection.EndLine);

			textBoxChangeset.Text = teamSetting.ChangeSet;

		}

		private void BindToolTips()
		{
			toolTip1.SetToolTip(_cmbQuery, "Select a Team Query to find a Work Item used for code review.");
			toolTip1.SetToolTip(_lblQueryFrom, toolTip1.GetToolTip(_cmbQuery));

			toolTip1.SetToolTip(_cmbLinkedItem, "Select a Work Item to associate with your new Code Review Response.");
			toolTip1.SetToolTip(_lblAssociateWith, toolTip1.GetToolTip(_cmbLinkedItem));

			toolTip1.SetToolTip(_cmbAssignedUser, "Select a TFS 'Contributor' or 'Project Administrator.'");
			toolTip1.SetToolTip(_lblAssignedUser, toolTip1.GetToolTip(_cmbAssignedUser));

			toolTip1.SetToolTip(_txtTitle, "Enter a title for your new Code Review Response.");
			toolTip1.SetToolTip(_lblTitle, toolTip1.GetToolTip(_txtTitle));

			toolTip1.SetToolTip(_txtDescription, "Enter why would you like the code to change.");
			toolTip1.SetToolTip(_lblDescription, toolTip1.GetToolTip(_txtDescription));

			toolTip1.SetToolTip(_txtResolution, "Enter how would you like the code to change.");
			toolTip1.SetToolTip(_lblResolution, toolTip1.GetToolTip(_txtResolution));
		}

		#endregion



		#region Data Validation

		private bool ValidateAssignedUser()
		{
			if (_cmbAssignedUser.SelectedItem != null)
				return true;

			if (!String.IsNullOrEmpty(_cmbAssignedUser.Text))
			{
				for (int i = 0; i < _cmbAssignedUser.Items.Count; i++)
				{
					if (_cmbAssignedUser.Items[i].ToString().Equals(_cmbAssignedUser.Text))
					{
						_cmbAssignedUser.SelectedIndex = i;
						return true;
					}
				}
			}

			_errorProvider1.SetError(_lblAssignedUser, String.Format("You must assign a user to the new {0} work item", Constants.CODE_REVIEW_ITEM_TYPE));
			return false;
		}


		private bool ValidatePriority()
		{
			if (_cmbPriority.Items.Count == 0 || _cmbPriority.SelectedIndex >= 0)
				return true;

			_errorProvider1.SetError(_lblPriority, String.Format("You must supply a Priority for the new {0} work item", Constants.CODE_REVIEW_ITEM_TYPE));
			return false;
		}

		private bool ValidateTitle()
		{
			if (!String.IsNullOrEmpty(_txtTitle.Text))
				return true;

			_errorProvider1.SetError(_lblTitle, String.Format("You must supply a Title for the new {0} work item", Constants.CODE_REVIEW_ITEM_TYPE));
			return false;
		}


		private bool ValidateDescription()
		{
			if (!String.IsNullOrEmpty(_txtDescription.Text))
				return true;

			_errorProvider1.SetError(_lblDescription, String.Format("You must supply a Description for the new {0} work item", Constants.CODE_REVIEW_ITEM_TYPE));
			return false;
		}


		#endregion

		#region Work Item Submission

		private void AddChangesetLink(WorkItem workItem)
		{
			try
			{

				if (!string.IsNullOrEmpty(textBoxChangeset.Text))
				{
					// Add a source code file linkage for the changeset to the workitem
					RegisteredLinkTypeCollection linkTypes = TFSService.WorkItemStore.RegisteredLinkTypes;
					RegisteredLinkType linkType = linkTypes["Source Code File"];
					if (linkType != null)
					{

						Changeset reviewChangeset = TFSService.VersionControlServer.GetChangeset(Int32.Parse(textBoxChangeset.Text));
						if (reviewChangeset != null)
						{
							ExternalLink changeLink = new ExternalLink(linkType, reviewChangeset.ArtifactUri.AbsoluteUri);
							workItem.Links.Add(changeLink);
						}

					}
				}
			}
			catch (Exception e)
			{
				Instrumentation.Log(e);
			}
		}

		private void AddRelation(WorkItem workItem)
		{
			WorkItem relatedItem = GetRelatedCodeReview();

			if (relatedItem == null)
				return;

			workItem.Links.Add(new RelatedLink(relatedItem.Id));

			if (relatedItem.Fields[Constants.AREA_FIELD] != null)
				workItem.Fields[Constants.AREA_FIELD].Value = relatedItem.Fields[Constants.AREA_FIELD].Value;

			if (relatedItem.Fields[Constants.ITERATION_FIELD] != null)
				workItem.Fields[Constants.ITERATION_FIELD].Value = relatedItem.Fields[Constants.ITERATION_FIELD].Value;

		}


		private WorkItem CreateWorkItem(Project project)
		{
			string reason;
			if (!HasWorkItemDefinition(project, out reason))
			{
				DirectionsForm directions = new DirectionsForm(reason);
				directions.ShowDialog();
				return null;
			}
			WorkItemType wiType = project.WorkItemTypes[Constants.CODE_REVIEW_ITEM_TYPE];
			return wiType.NewWorkItem();
		}

		private void Save()
		{
			Project project = workItemSubmissionProject;

			//if (workItemSubmissionProject == null)
			//	project  = TFSService.WorkItemStore.Projects[TFSService.TeamProject.Name];
			
			WorkItem workItem = CreateWorkItem(project);
			if (workItem == null)
				return;

			try
			{
				workItem.Fields[Constants.CODE_DESCRIPTION_FIELD].Value = _txtDescription.Text;
				workItem.Fields[Constants.CODE_RESOLUTION_FIELD].Value = _txtResolution.Text;
				workItem.Fields[Constants.CODE_SNIPPET_FIELD].Value = _codeSelection.Snippet;
				workItem.Fields[Constants.CODE_FILE_FIELD].Value = _codeSelection.CodeFile;
				workItem.Fields[Constants.CODE_MEMBER_FIELD].Value = _codeSelection.CodeMember;
				workItem.Fields[Constants.CODE_PROJECT_FIELD].Value = _codeSelection.CodeProject;
				workItem.Fields[Constants.CODE_START_LINE_FIELD].Value = _codeSelection.StartLine.ToString();
				workItem.Fields[Constants.CODE_END_LINE_FIELD].Value = _codeSelection.EndLine.ToString();
				workItem.Fields[Constants.CODE_CLASS_FIELD].Value = _codeSelection.CodeClass;
				workItem.Fields[Constants.CODE_NAMESPACE_FIELD].Value = _codeSelection.Namespace;
				workItem.Fields[Constants.ASSIGNED_TO_FIELD].Value = _cmbAssignedUser.SelectedItem.ToString();
				workItem.Fields[Constants.TITLE_FIELD].Value = _txtTitle.Text;
				if (_cmbPriority.SelectedItem != null)
					workItem.Fields[Constants.CODE_PRIORITY_FIELD].Value = _cmbPriority.SelectedItem;

				AddChangesetLink(workItem);
				AddRelation(workItem);

				workItem.Save();

				string success = String.Format("#{0} created and assigned to {1} in Team Project {2}",
												 workItem.Id,
												 workItem.Fields[Constants.ASSIGNED_TO_FIELD].Value,
												 workItem.Project.Name);
				shellDTE.StatusBar.Text = success;
				Instrumentation.Trace(success);

				UpdateWindowSettings();
				UpdateTeamSettings();
				settings.Save();

			}
			catch (Exception e)
			{
				string message = "Field validation failed for the work item. Please file a defect with reproduceable steps at http://TeamReview.codeplex.com/";
				Instrumentation.Log(e);
				_errorProvider1.SetError(_lblBtnSave, message);
			}
		}

		#endregion


		private bool HasWorkItemDefinition(Project project, out string reason)
		{
			reason = string.Empty;
			if (!TFSService.WorkItemStore.Projects[project.Name].WorkItemTypes.Contains(Constants.CODE_REVIEW_ITEM_TYPE))
			{
				reason = String.Format(
						"The project '{0}' does not have the work item type '{1}.' The '{0}' project may be used because it's where the Associated Item is stored or the source of the Query you selected",
						project.Name,
						Constants.CODE_REVIEW_ITEM_TYPE);
				Instrumentation.Trace(reason);
						
				return false;
			}
			return true;
		}


		private WorkItem GetRelatedCodeReview()
		{
			if (_cmbLinkedItem.SelectedIndex < 0)
				return null;

			return TFSService.WorkItemStore.GetWorkItem(((BindingItemCodeReview)_cmbLinkedItem.SelectedItem).Id);
		}



	}
}